package com.example.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.constants.WxNotifyConstants;
import com.example.constants.WxPayConstants;
import com.example.domain.Order;
import com.example.dto.WxChatBasePayDto;
import com.example.dto.WxChatPayDto;
import com.example.dto.WxChatRefundDto;
import com.example.mapper.OrderMapper;
import com.example.service.WxOrderService;
import com.example.utils.*;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.security.Signature;
import java.util.*;

@Service
public class WxOrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements WxOrderService {

    @Resource
    private RedisCache redisCache;
    @Resource
    private WxPayConstants wxPayConfig;
    @Resource
    private CloseableHttpClient wxPayClient;
    @Resource
    private WxPayCallbackUtil wxPayCallbackUtil;
    @Resource
    private WxPayConstants wxPayConstants;
    @Resource
    private CloseableHttpClient closeableHttpClient;

    /**
     * 生成订单
     */
    @Override
    public Result addWxOrder(Order order) {
        try {
            //生成订单
            order.setOrderStatus(1);
            Date now = new Date();
            order.setCreateTime(now);
            //订单过期时间 30分钟
            long time = 30*60*1000;//30分钟
            Date afterDate = new Date(now.getTime() + time);//30分钟后的时间
            order.setOutTime(afterDate);
            baseMapper.insert(order);

            //订单号自己写
            String orderNumber = "1589685962356895";
            order.setOrderNumber(orderNumber);
            int i = baseMapper.updateById(order);

            //支付
            WxChatBasePayDto payData = new WxChatBasePayDto();
            payData.setTitle("跑腿订单支付");
            payData.setOrderId(order.getOrderNumber());
            payData.setPrice(new BigDecimal(order.getOrderAmount()).divide(new BigDecimal(100)));
            payData.setNotify(WxNotifyConstants.RUN_ERRANDS_NOTIFY);
            payData.setOpenId(order.getOpenId());
            WxChatPayDto pay = pay(payData);

            return i != 0 ? Result.ok("订单生成成功").put("one",order).put("wxDto",pay) : Result.error("订单生成失败");
        }catch (Exception e){
            e.printStackTrace();
            return Result.error("订单生成失败");
        }
    }

    /**
     * 微信用户调用微信支付
     */
    @Override
    public WxChatPayDto pay(WxChatBasePayDto payData) {

        String prepayId = WxPayCommon.wxJsApiPay(wxPayConfig, payData, payData.getOpenId(), wxPayClient);

        WxChatPayDto wxChatPayDto = new WxChatPayDto();
        wxChatPayDto.setAppid(redisCache.getCacheObject("WX_PAY_SAVE_WX_APPID"));
        wxChatPayDto.setTimeStamp(String.valueOf(System.currentTimeMillis() / 1000));
        wxChatPayDto.setNonceStr(UUID.randomUUID().toString().replaceAll("-", ""));
        wxChatPayDto.setPrepayId("prepay_id=" + prepayId);
        wxChatPayDto.setSignType("RSA");
        wxChatPayDto.setPaySign(getSign(wxChatPayDto.getNonceStr(),wxChatPayDto.getAppid(),wxChatPayDto.getPrepayId(),Long.parseLong(wxChatPayDto.getTimeStamp())));

        return wxChatPayDto;
    }

    /**
     * 订单支付后回调
     */
    @Override
    public Map<String, String> wxOrderCallBack(HttpServletRequest request, HttpServletResponse response) {
        Map<String, String> map = new HashMap<>(2);
        try {
            Map<String, String> stringMap = wxPayCallbackUtil.wxChatPayCallback(request, response);
            //支付成功
            if (stringMap.get("trade_state").equals("SUCCESS")){
                //获取订单号
                String outTradeNo = stringMap.get("out_trade_no");
                Order orderNumber = baseMapper.selectOne(new QueryWrapper<Order>().eq("order_number", outTradeNo));
                orderNumber.setOrderStatus(2);
                orderNumber.setPayTime(new Date());
                baseMapper.updateById(orderNumber);
            }
            //响应微信
            map.put("code", "SUCCESS");
            map.put("message", "成功");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

    /**
     * 订单退款
     */
    @Override
    public Result orderRefund(Integer id) {
        try {
            Order order = baseMapper.selectById(id);
            WxChatRefundDto dto = new WxChatRefundDto();
            dto.setOrderId(order.getOrderNumber());
            String refundOrderId = IdWorker.getIdStr();
            dto.setRefundOrderId(refundOrderId);
            dto.setNotify(WxNotifyConstants.CAMPUS_CARD_REFUND_NOTIFY);
            dto.setRefundMoney(new BigDecimal(order.getOrderAmount()));
            dto.setTotalMoney(new BigDecimal(order.getOrderAmount()));
            String s = WxPayRefundUtil.refundPay(wxPayConstants, dto, closeableHttpClient);
//            System.out.println(s);
            return Result.ok("退款成功");
        }catch (Exception e){
            e.printStackTrace();
            return Result.error("退款失败");
        }
    }

    /**
     * 获取签名
     * @param nonceStr  随机数
     * @param appId     微信公众号或者小程序等的appid
     * @param prepay_id 预支付交易会话ID
     * @param timestamp 时间戳 10位
     * @return String 新签名
     */
    String getSign(String nonceStr, String appId, String prepay_id, long timestamp) {

        //从下往上依次生成
        String message = buildMessage(appId, timestamp, nonceStr, prepay_id);
        //签名
        try {
            return sign(message.getBytes("utf-8"));
        } catch (IOException e) {
            throw new RuntimeException("签名异常,请检查参数或商户私钥");
        }
    }

    String sign(byte[] message) {
        try {
            //签名方式
            Signature sign = Signature.getInstance("SHA256withRSA");
            //私钥，通过MyPrivateKey来获取，这是个静态类可以接调用方法 ，需要的是_key.pem文件的绝对路径配上文件名
            sign.initSign(PemUtil.loadPrivateKey(new FileInputStream(redisCache.getCacheObject("WX_PAY_SAVE_PRIVATE_KEY_PATH").toString())));
            sign.update(message);
            return Base64.getEncoder().encodeToString(sign.sign());
        } catch (Exception e) {
            throw new RuntimeException("签名异常,请检查参数或商户私钥");
        }
    }

    /**
     * 按照前端签名文档规范进行排序，\n是换行
     *
     * @param nonceStr  随机数
     * @param appId     微信公众号或者小程序等的appid
     * @param prepay_id 预支付交易会话ID
     * @param timestamp 时间戳 10位
     * @return String 新签名
     */
    String buildMessage(String appId, long timestamp, String nonceStr, String prepay_id) {
        return appId + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + prepay_id + "\n";
    }
}
